package sk.ogame.brain;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import sk.ogame.entity.Planet;
import sk.ogame.entity.PlanetOverview;
import sk.ogame.entity.Position;
import sk.ogame.realm.exception.PlanetNoInfoException;
import sk.ogame.realm.utils.TimeUtil;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.*;
import java.util.*;

import static sk.ogame.realm.exception.PlanetNoInfoException.CODE_OVERVIEW;
import static sk.ogame.realm.exception.PlanetNoInfoException.CODE_PLANET;

/**
 * @author 支绍昆
 * 2023/2/17
 * 记忆
 **/
@Service
public class Memory implements Serializable {

    private static final long serialVersionUID = 3427503221458183000L;
    private static final Logger logger = LoggerFactory.getLogger(Memory.class);

    private int currentPlanet = 0;
    private int masterPlanet = 310879;

    //<planetId, info>
    private Map<Integer, PlanetOverview> overviewMap = new HashMap<>();
    private Map<Integer, Planet> planetMap = new HashMap<>();
    private Map<Integer, Position> positionMap = new HashMap<>();

    @PostConstruct
    private void init() {
        this.readFromFile();
        logger.info("memory init overview:[{}] planet:[{}] position:[{}] ", overviewMap.size(), planetMap.size(), positionMap.size());

        new Timer("save memory").schedule(new TimerTask() {
            @Override
            public void run() {
                saveToFile();
            }
        }, 1000L * 10, 1000L * 10);
    }

    @PreDestroy
    private void destroy() {
        logger.info("destroy bean to save Memory.");
        this.saveToFile();
    }

    public Planet getPlanet(int planetId) {
        if (planetMap.containsKey(planetId)) {
            return planetMap.get(planetId);
        }
        throw new PlanetNoInfoException("can not get planet info : " + planetId, CODE_PLANET, planetId);
    }

    public Planet getPlanetInPeriod(int planetId) {
        if (planetMap.containsKey(planetId)) {
            Planet planet = planetMap.get(planetId);
            if (TimeUtil.inPeriod(planet.getUsertime())){
                return planet;
            }
        }
        throw new PlanetNoInfoException("can not get planet info : " + planetId, CODE_PLANET, planetId);
    }

    public void putPlanet(Planet planet) {
        planetMap.put(planet.getPid(), planet);
    }

    public void putPlanetOverview(List<PlanetOverview> list) {
        for (PlanetOverview ov : list) {
            overviewMap.put(ov.getPid(), ov);
        }
    }

    public Position getPosition(int planetId) {
        if (positionMap.containsKey(planetId)) {
            return positionMap.get(planetId);
        }
        if (overviewMap.containsKey(planetId)) {
            PlanetOverview overview = overviewMap.get(planetId);
            String[] split = overview.getPosition().split(":");
            Position newPosition = new Position(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
            positionMap.put(planetId, newPosition);
            return newPosition;
        }
        throw new PlanetNoInfoException("can not get position : " + planetId, CODE_OVERVIEW);
    }

    //----------------------------- persistent -----------------------------------

    public void saveToFile() {
        try {
            File memoryFile = getMemoryFile();
            if (!memoryFile.exists()) {
                if (!memoryFile.getParentFile().exists()) {
                    memoryFile.getParentFile().mkdirs();
                }
                memoryFile.createNewFile();
            }

            FileOutputStream fileOutputStream = new FileOutputStream(memoryFile);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(this);
            objectOutputStream.flush();
            objectOutputStream.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void readFromFile() {
        File memoryFile = getMemoryFile();
        if (!memoryFile.exists()) {
            return;
        }

        try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(memoryFile))) {
            Memory diskMemory = (Memory) objectInputStream.readObject();
            this.planetMap = diskMemory.getPlanetMap();
            this.overviewMap = diskMemory.getOverviewMap();
            this.positionMap = diskMemory.getPositionMap();
        } catch (IOException|ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    private static File getMemoryFile() {
        String userDir = System.getProperty("user.dir");
        String path = userDir + File.separator + "data" + File.separator + "memory";
        return new File(path);
    }

    //----------------------------- get set --------------------------------------

    public int getCurrentPlanet() {
        return currentPlanet;
    }

    public void setCurrentPlanet(int currentPlanet) {
        this.currentPlanet = currentPlanet;
    }

    public int getMasterPlanet() {
        return masterPlanet;
    }

    public Map<Integer, Planet> getPlanetMap() {
        return planetMap;
    }

    public Map<Integer, PlanetOverview> getOverviewMap() {
        return overviewMap;
    }

    public Map<Integer, Position> getPositionMap() {
        return positionMap;
    }
}
